#pragma once

#include "Board.h"
#include <vector>
#include "MoveGenerator.h"
#include <algorithm>

inline void sort_moves(std::vector<Move> &moves)
{
	if (moves.empty())
		return;

	PieceColor color = moves[0].Piece1.Color;

	std::sort(moves.begin(), moves.end(),
	[color](const Move &a, const Move &b) -> bool
	{
		bool aMate = a.Flags & (BLACK_MATE | WHITE_MATE),
			 bMate = b.Flags & (BLACK_MATE | WHITE_MATE);

		if (aMate)
		{
			if (!bMate)
				return true;
		}
		else if (bMate)
			return false;

		if (a.Type == MoveType::Capture)
		{
			if (b.Type != MoveType::Capture)
				return true;
		}
		else if (b.Type == MoveType::Capture)
			return false;

		bool aForward = false, bForward = false;
		if (color == PieceColor::White)
		{
			if (a.DestPos1 < a.SrcPos1)
				aForward = true;
			if (b.DestPos1 < b.SrcPos1)
				bForward = true;
		}
		else
		{
			if (a.DestPos1 > a.SrcPos1)
				aForward = true;
			if (b.DestPos1 > b.SrcPos1)
				bForward = true;
		}

		if (aForward)
		{
			if (!bForward)
				return true;
		}
		else if (bForward)
			return false;

		return a.DestPos1 < b.DestPos1;
	});
}

template<typename Fn>
Move Negamax(Board &b, int depthLimit, const Fn &fn)
{
	Move *bestMove;
	int bestValue = INT_MIN;

	int color = (int) b.WhoseMove;

	std::vector<Move> moves;
	moves.reserve(30);

	MoveGenerator().Generate(b, nullptr, moves);
	sort_moves(moves);

	for (Move &m : moves)
	{
		b.DoMove(m);

		int val = -Negamax_Recurse(b, m, depthLimit - 1, INT_MIN, INT_MAX, -color, fn);

		if (val > bestValue)
		{
			bestMove = &m;
			bestValue = val;
		}

		b.UndoMove(m);
	}

	return *bestMove;
}

template<typename Fn>
int Negamax_Recurse(Board &b, Move &lastMove, int depth, int alpha, int beta, int color, const Fn &fn)
{
	static const int MAX_SCORE = 16384;

	if (b.IsStalemate())
		return 0;
	else if (b.IsWhiteMate())
		return -color * MAX_SCORE;
	else if (b.IsBlackMate())
		return color * MAX_SCORE;
	else if (depth == 0)
	{
		if (lastMove.Type == MoveType::Capture)
			return Negamax_Quiescent(b, lastMove, alpha, beta, color, fn);
		else
			return color * fn(b, lastMove);
	}

	int bestValue = INT_MIN;

	std::vector<Move> moves;
	moves.reserve(30);

	MoveGenerator().Generate(b, &lastMove, moves);
	sort_moves(moves);

	for (Move &m : moves)
	{
		b.DoMove(m);

		int val = -Negamax_Recurse(b, m, depth - 1, -beta, -alpha, -color, fn);

		b.UndoMove(m);

		if (val > bestValue)
			bestValue = val;
		if (val > alpha)
			alpha = val;
		if (alpha >= beta)
			break;
	}

	return bestValue;
}

template<typename Fn>
int Negamax_Quiescent(Board &b, Move &lastMove, int alpha, int beta, int color, const Fn &fn)
{
	static const int MAX_SCORE = 16384;

	if (b.IsStalemate())
		return 0;
	else if (b.IsWhiteMate())
		return -color * MAX_SCORE;
	else if (b.IsBlackMate())
		return color * MAX_SCORE;
	else if (lastMove.Type != MoveType::Capture)
		return color * fn(b, lastMove);

	int bestValue = INT_MIN;

	std::vector<Move> moves;
	moves.reserve(30);

	MoveGenerator().Generate(b, &lastMove, moves);
	sort_moves(moves);

	for (Move &m : moves)
	{
		b.DoMove(m);

		int val = -Negamax_Quiescent(b, m, -beta, -alpha, -color, fn);

		b.UndoMove(m);

		if (val > bestValue)
			bestValue = val;
		if (val > alpha)
			alpha = val;
		if (alpha >= beta)
			break;
	}

	return bestValue;
}